//! gRPC client for connecting to entsrv

use crate::entity_service::entity_service_client::EntityServiceClient;
use crate::entity_service::*;
use crate::{PartyModConfig, PartyModError, Result};
use std::time::Duration;
use tonic::transport::Channel;
use tracing::{debug, info, warn};

/// Entity service client wrapper
#[derive(Debug, Clone)]
pub struct EntityClient {
    client: EntityServiceClient<Channel>,
    config: PartyModConfig,
}

impl EntityClient {
    /// Create a new EntityClient
    pub async fn new(config: PartyModConfig) -> Result<Self> {
        let endpoint = format!("http://{}:{}", config.entsrv_host, config.entsrv_port);
        info!("Connecting to entsrv at {}", endpoint);
        
        let channel = Channel::from_shared(endpoint)
            .map_err(|e| PartyModError::ConnectionError(e.to_string()))?
            .timeout(Duration::from_secs(config.timeout_seconds))
            .connect()
            .await
            .map_err(|e| PartyModError::ConnectionError(e.to_string()))?;
        
        let client = EntityServiceClient::new(channel);
        
        Ok(Self { client, config })
    }
    
    /// Get all entity definitions
    pub async fn get_all_entities(&mut self) -> Result<Vec<EntityDefinition>> {
        debug!("Fetching all entity definitions");
        
        let request = tonic::Request::new(GetAllEntitiesRequest {});
        let response = self.client.get_all_entities(request).await?;
        
        Ok(response.into_inner().entities)
    }
    
    /// Get a specific entity definition
    pub async fn get_entity(&mut self, entity_name: &str) -> Result<EntityDefinition> {
        debug!("Fetching entity definition for: {}", entity_name);
        
        let request = tonic::Request::new(GetEntityRequest {
            entity_name: entity_name.to_string(),
        });
        
        let response = self.client.get_entity(request).await?;
        Ok(response.into_inner().entity.unwrap())
    }
    
    /// Get all view entity definitions
    pub async fn get_all_view_entities(&mut self) -> Result<Vec<ViewEntityDefinition>> {
        debug!("Fetching all view entity definitions");
        
        let request = tonic::Request::new(GetAllViewEntitiesRequest {});
        let response = self.client.get_all_view_entities(request).await?;
        
        Ok(response.into_inner().view_entities)
    }
    
    /// Query data from an entity
    pub async fn query_data(
        &mut self,
        entity_name: &str,
        fields: Option<Vec<String>>,
        conditions: Option<Vec<crate::models::QueryCondition>>,
        order_by: Option<String>,
        limit: Option<i32>,
        offset: Option<i32>,
    ) -> Result<Vec<DataRow>> {
        debug!("Querying data from entity: {}", entity_name);
        
        let grpc_conditions = conditions
            .unwrap_or_default()
            .into_iter()
            .map(|c| c.into())
            .collect();
        
        let request = tonic::Request::new(QueryDataRequest {
            entity_name: entity_name.to_string(),
            fields: fields.unwrap_or_default(),
            conditions: grpc_conditions,
            order_by: order_by.unwrap_or_default(),
            limit: limit.unwrap_or(100),
            offset: offset.unwrap_or(0),
        });
        
        let response = self.client.query_data(request).await?;
        Ok(response.into_inner().rows)
    }
    
    /// Insert data into an entity
    pub async fn insert_data(
        &mut self,
        entity_name: &str,
        data: std::collections::HashMap<String, String>,
    ) -> Result<String> {
        debug!("Inserting data into entity: {}", entity_name);
        
        let request = tonic::Request::new(InsertDataRequest {
            entity_name: entity_name.to_string(),
            data,
        });
        
        let response = self.client.insert_data(request).await?;
        let result = response.into_inner();
        
        if result.success {
            Ok(result.generated_id)
        } else {
            Err(Box::new(PartyModError::Other(result.message)))
        }
    }
    
    /// Update data in an entity
    pub async fn update_data(
        &mut self,
        entity_name: &str,
        data: std::collections::HashMap<String, String>,
        conditions: Vec<crate::models::QueryCondition>,
    ) -> Result<i32> {
        debug!("Updating data in entity: {}", entity_name);
        
        let grpc_conditions = conditions
            .into_iter()
            .map(|c| c.into())
            .collect();
        
        let request = tonic::Request::new(UpdateDataRequest {
            entity_name: entity_name.to_string(),
            data,
            conditions: grpc_conditions,
        });
        
        let response = self.client.update_data(request).await?;
        let result = response.into_inner();
        
        if result.success {
            Ok(result.affected_rows)
        } else {
            Err(Box::new(PartyModError::Other(result.message)))
        }
    }
    
    /// Delete data from an entity
    pub async fn delete_data(
        &mut self,
        entity_name: &str,
        conditions: Vec<crate::models::QueryCondition>,
    ) -> Result<i32> {
        debug!("Deleting data from entity: {}", entity_name);
        
        let grpc_conditions = conditions
            .into_iter()
            .map(|c| c.into())
            .collect();
        
        let request = tonic::Request::new(DeleteDataRequest {
            entity_name: entity_name.to_string(),
            conditions: grpc_conditions,
        });
        
        let response = self.client.delete_data(request).await?;
        let result = response.into_inner();
        
        if result.success {
            Ok(result.affected_rows)
        } else {
            Err(Box::new(PartyModError::Other(result.message)))
        }
    }
    
    /// Create table for an entity
    pub async fn create_table(&mut self, entity_name: &str, drop_if_exists: bool) -> Result<()> {
        debug!("Creating table for entity: {}", entity_name);
        
        let request = tonic::Request::new(CreateTableRequest {
            entity_name: entity_name.to_string(),
            drop_if_exists,
        });
        
        let response = self.client.create_table(request).await?;
        let result = response.into_inner();
        
        if result.success {
            Ok(())
        } else {
            Err(Box::new(PartyModError::Other(result.message)))
        }
    }
}